#include <iostream>
using namespace std;
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/epoll.h>
#include <fcntl.h>

int SetNonBlock(int sockfd)
{
	if (fcntl(sockfd, F_SETFL, fcntl(sockfd, F_GETFD, 0)|O_NONBLOCK) == -1)
	{
		return -1;
	}
	return 0;
}

int main( int argc, char * argv[] )
{
	if (argc<3)
	{
		cout<<"argv[1] host ip"<<endl;
		cout<<"argv[2] port"<<endl;
		exit(0);
	}

	int epfd,listenfd, connfd, sockfd, nfds, n;
	socklen_t clilen;
	char line[512] = {0};
	epfd = epoll_create(1024);

	struct sockaddr_in cliaddr;
	struct sockaddr_in svraddr;
	struct epoll_event ev, events[20];

	listenfd = socket( AF_INET, SOCK_STREAM, 0 );

	SetNonBlock( listenfd );

	ev.data.fd = listenfd;
	ev.events = EPOLLIN|EPOLLET;

	epoll_ctl(epfd, EPOLL_CTL_ADD, listenfd, &ev);

	bzero( &svraddr, sizeof(sockaddr_in) );

	svraddr.sin_family = AF_INET;
	inet_aton( argv[1], &(svraddr.sin_addr) );
	svraddr.sin_port = htons( atoi(argv[2]) );
	bind( listenfd, (sockaddr *)&svraddr, sizeof(svraddr) );
	listen(listenfd,10);

	for(;;)
	{
		nfds = epoll_wait( epfd, events, 20, 500);

		for (int i=0; i<nfds; ++i)
		{
			if ( events[i].data.fd == listenfd )
			{
				connfd = accept( listenfd, (sockaddr *)&cliaddr, &clilen );
				if ( connfd < 0 )
				{
					perror( "error" );
					exit(0);
				}
				SetNonBlock(connfd);
				cout<<"accept a client, use fd["<< connfd <<"]"<<endl;

				ev.data.fd = connfd;
				ev.events = EPOLLIN|EPOLLET;

				epoll_ctl(epfd, EPOLL_CTL_ADD, connfd, &ev);
			}
			else if (events[i].events & EPOLLIN )
			{
				if ( (sockfd = events[i].data.fd) < 0) continue;

				n = read(sockfd, line, 512 );
				if ( n <= 0)
			   	{
					close(sockfd);
					ev.data.fd = sockfd;
					ev.events = EPOLLIN|EPOLLET;
					epoll_ctl(epfd,EPOLL_CTL_DEL,sockfd,&ev);

					events[i].data.fd = -1;
					cout<<"read line'length["<<n<<"] server close fd["<<sockfd<<"]"<<endl;
				}
				else //n>0
				{
					cout<<"client say: "<< line <<endl;
					bzero( line, sizeof(line) );
					//设置用于写操作的文件描述符
					ev.data.fd=sockfd;
					//设置用于注测的写操作事件
					ev.events=EPOLLOUT|EPOLLET;
					//修改sockfd上要处理的事件为EPOLLOUT
					epoll_ctl(epfd,EPOLL_CTL_MOD,sockfd,&ev);
				}
			}
			else if(events[i].events&EPOLLOUT)
			{
				printf("send data to client\n");
				char lineReturn[] = "hello, cao ni ma\n";
				sockfd = events[i].data.fd;
				int n = write(sockfd, lineReturn, sizeof(lineReturn));
				if(n < 0){
					cout<<"write error\n";
				}
				char lineReturn2[] = "hello, cao ni ma2\n";
				n = write(sockfd, lineReturn2, sizeof(lineReturn2));
				if(n < 0){
					cout<<"write error\n";
				}
				//设置用于读操作的文件描述符
				ev.data.fd=sockfd;
				//设置用于注测的读操作事件
				ev.events=EPOLLOUT|EPOLLET;
				//修改sockfd上要处理的事件为EPOLIN
				epoll_ctl(epfd,EPOLL_CTL_DEL,sockfd,&ev);
				close(sockfd);
				printf("close socket after write\n");
			}
		}
	}

}
